use std::str::FromStr;
use std::{io, u32};
use std::collections::HashMap;
use std::fs::File;
use std::io::BufRead;

const DAYS_IN_MONTHS: [u32; 12] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];

pub fn time_info(time: &str) -> String {
    let date: Vec<&str> = time.split("-").collect();
    let mut ret = vec![];
    let year = u32::from_str(date[0]).unwrap();
    let month = u32::from_str(date[1]).unwrap();
    let day = u32::from_str(date[2]).unwrap();

    let days_of_year = get_days_of_year(year, month, day);
    let left_days = get_left_days(year, days_of_year);
    let current_week = get_current_week_num(year, days_of_year, left_days);

    let days_until_to_spring = get_days_until_to_spring(year, month, day, left_days);

    ret.push(current_week.to_string());
    ret.push(left_days.to_string());
    ret.push(days_until_to_spring.to_string());
    ret.join(",")
}

fn get_current_week_num(year: u32, days_of_year: u32, left_days: u32) -> u32 {
    let last_day_week = calculate_weekday(year, 12, 31);
    if left_days <= last_day_week {
        return 1;
    }
    let week = calculate_weekday(year, 1, 1);
    if week <= 4 {
        (days_of_year + week + 5) / 7
    } else {
        (days_of_year + week + 6) / 7 - 1
    }
}

fn get_days_until_to_spring(year: u32, month: u32, day: u32, left_days: u32) -> u32 {
    let spring_festival_map = generate_spring_festival_map();

    fn get_spring_month_and_day(date: &String) -> (u32, u32) {
        let date: Vec<&str> = date.split("-").collect();
        let spring_month = u32::from_str(date[0]).unwrap();
        let spring_day = u32::from_str(date[1]).unwrap();
        (spring_month, spring_day)
    }
    if let Some(date) = spring_festival_map.get(&year) {
        let (spring_month, spring_day) = get_spring_month_and_day(&date);
        if spring_month >= month && spring_day >= day {
            return get_days_of_year(year, spring_month, spring_day) -
                get_days_of_year(year, month, day) - 1;
        }
    }
    if let Some(date) = spring_festival_map.get(&(year + 1)) {
        let (spring_month, spring_day) = get_spring_month_and_day(&date);
        return left_days + get_days_of_year(year + 1, spring_month, spring_day - 1);
    }
    0
}

fn generate_spring_festival_map() -> HashMap<u32, String> {
    let mut spring_festival_map: HashMap<u32, String> = HashMap::new();
    let file = File::open("spring_festival_date").unwrap();
    let reader = io::BufReader::new(file);
    for line in reader.lines() {
        let line = line.unwrap();
        let parts: Vec<&str> = line.split("\t").filter(|c| !c.is_empty()).collect();
        let year = u32::from_str(parts[0]).unwrap();
        let date = parts[1].to_string();
        spring_festival_map.insert(year, date.clone());
    }
    spring_festival_map
}

fn calculate_weekday(year: u32, month: u32, day: u32) -> u32 {
    let mut year = year as i32;
    let mut month = month as i32;
    if month < 3 {
        month += 12;
        year -= 1;
    }
    let c = year / 100;
    let y = year % 100;
    // (y + [y / 4] + [c / 4] - 2c + [26 (m + 1) / 10] + d - 1) % 7
    let mut weekday = y + (y / 4) + (c / 4) - (2 * c) + ((13 * (month + 1)) / 5) + day as i32 - 1;
    while weekday < 0 {
        weekday += 7;
    }
    (weekday % 7) as u32
}

fn get_left_days(year: u32, passed_days: u32) -> u32 {
    if is_leap_year(year) {
        366 - passed_days
    } else {
        365 - passed_days
    }
}

fn get_days_of_year(year: u32, month: u32, day: u32) -> u32 {
    let mut days = 0;
    days += day;
    for m in 0..month - 1 {
        let m = m as usize;
        days += DAYS_IN_MONTHS[m];
    }
    if month > 2 && is_leap_year(year) {
        days += 1;
    }
    days
}

fn is_leap_year(year: u32) -> bool {
    (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)
}